List comprehensions and Generator Expressions

List comprehensions and Readibility


In [1]:
symbols = '$¢£¥€Ω'

In [2]:
codes = []

In [3]:
for symbol in symbols:
    codes.append(ord(symbol))

In [4]:
codes


Out[4]:
[36, 162, 163, 165, 8364, 937]

In [5]:
codes = [ord(symbol) for symbol in symbols]

In [6]:
codes


Out[6]:
[36, 162, 163, 165, 8364, 937]

Listcomps Versus map and filter


In [ ]:
symbols = '$¢£¥€Ω'

In [7]:
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]

In [8]:
beyond_ascii


Out[8]:
[162, 163, 165, 8364, 937]

In [9]:
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))

In [10]:
beyond_ascii


Out[10]:
[162, 163, 165, 8364, 937]

In [14]:
list(filter(lambda c: c > 127, list(map(ord, symbols))))


Out[14]:
[162, 163, 165, 8364, 937]

Carsesian Products


In [1]:
colors = ['black', 'white']

In [2]:
sizes = ['S', 'M', 'L']

In [3]:
tshirts = [(color, size) for color in colors for size in sizes]

In [4]:
tshirts


Out[4]:
[('black', 'S'),
 ('black', 'M'),
 ('black', 'L'),
 ('white', 'S'),
 ('white', 'M'),
 ('white', 'L')]

In [5]:
for color in colors:
    for size in sizes:
        print((color, size))


('black', 'S')
('black', 'M')
('black', 'L')
('white', 'S')
('white', 'M')
('white', 'L')

In [6]:
tshirts = [(color, size) for size in sizes for color in colors]

In [7]:
tshirts


Out[7]:
[('black', 'S'),
 ('white', 'S'),
 ('black', 'M'),
 ('white', 'M'),
 ('black', 'L'),
 ('white', 'L')]

Generator Expressions


In [11]:
symbols = '$¢£¥€' + chr(164)

In [12]:
tuple(ord(symbol) for symbol in symbols)


Out[12]:
(36, 162, 163, 165, 8364, 164)

In [13]:
import array

In [15]:
array.array('I', (ord(symbol) for symbol in symbols))


Out[15]:
array('I', [36, 162, 163, 165, 8364, 164])

In [16]:
colors = ['black','white']

In [17]:
sizes = ['S','M','L']

In [21]:
for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes):
    print(tshirt)


black S
black M
black L
white S
white M
white L

Tuples Are Not Just Immutable Lists

Tuples as Records


In [22]:
lax_coordinates = (33.9425, -118.408056)

In [23]:
city, year, pop, chg, area = ('Tokyo',2003, 32450, 0.66, 8014)

In [24]:
traveler_ids = [('USA','31195855'), ('BRA', 'CE342567'),('ESP','XDA205856')]

In [25]:
for passport in sorted(traveler_ids):
    print('%s/%s' % passport)


BRA/CE342567
ESP/XDA205856
USA/31195855

In [27]:
for country, _ in traveler_ids:
    print(country)


USA
BRA
ESP

Tuple Unpacking


In [28]:
lax_coordinates = (33.9425, -118.408056)

In [29]:
latitude, longitude = lax_coordinates

In [30]:
latitude


Out[30]:
33.9425

In [31]:
longitude


Out[31]:
-118.408056

In [32]:
divmod(20,8)


Out[32]:
(2, 4)

In [33]:
t = (20, 8)

In [34]:
divmod(*t)


Out[34]:
(2, 4)

In [35]:
quotient, remainder = divmod(*t)

In [36]:
quotient, remainder


Out[36]:
(2, 4)

In [37]:
import os

In [38]:
_, filename = os.path.split('C:/Users/langestrst01/.ssh/id_rsa.pub')

In [39]:
filename


Out[39]:
'id_rsa.pub'

In [40]:
os.path.split('C:/Users/langestrst01/.ssh/id_rsa.pub')


Out[40]:
('C:/Users/langestrst01/.ssh', 'id_rsa.pub')

In [41]:
a, b, *rest = range(5)

In [42]:
a, b, rest


Out[42]:
(0, 1, [2, 3, 4])

In [46]:
a,b, *rest = range(3)

In [48]:
a, b, rest


Out[48]:
(0, 1, [2])

In [49]:
a, b, *rest = range(2)

In [50]:
a, b, rest


Out[50]:
(0, 1, [])

In [51]:
a, *body, c, d = range(5)

In [52]:
a, body, c, d


Out[52]:
(0, [1, 2], 3, 4)

In [53]:
*head, b, c, d = range(5)

In [54]:
head, b, c, d


Out[54]:
([0, 1], 2, 3, 4)

Nested tuple Unpacking


In [80]:
metro_areas = [
    ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
    ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
    ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
    ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
    ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]

In [81]:
print('{:15} | {:^9} | {:^9}'.format('', 'lat.', 'long.'))
fmt = '{:15} | {:9.4f} | {:9.4f}'
for name, cc, pop, (latitude, longitude) in metro_areas:
    if longitude <= 0:
        print(fmt.format(name, latitude, longitude))


                |   lat.    |   long.  
Mexico City     |   19.4333 |  -99.1333
New York-Newark |   40.8086 |  -74.0204
Sao Paulo       |  -23.5478 |  -46.6358

Named Tuples


In [82]:
from collections import namedtuple

In [83]:
City = namedtuple('City','name country population coordinates')

In [84]:
tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))

In [85]:
tokyo


Out[85]:
City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))

In [86]:
tokyo.population


Out[86]:
36.933

In [87]:
tokyo.coordinates


Out[87]:
(35.689722, 139.691667)

In [88]:
tokyo[1]


Out[88]:
'JP'

In [89]:
City._fields


Out[89]:
('name', 'country', 'population', 'coordinates')

In [90]:
LatLong = namedtuple('LatLong', 'lat long')

In [91]:
delhi_data = ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889))

In [92]:
delhi = City._make(delhi_data)

In [93]:
delhi._asdict()


Out[93]:
OrderedDict([('name', 'Delhi NCR'),
             ('country', 'IN'),
             ('population', 21.935),
             ('coordinates', (28.613889, 77.208889))])

In [97]:
for key, value in delhi._asdict().items():
    print(key + ':',value)


name: Delhi NCR
country: IN
population: 21.935
coordinates: (28.613889, 77.208889)

In [95]:
delhi


Out[95]:
City(name='Delhi NCR', country='IN', population=21.935, coordinates=(28.613889, 77.208889))

Slicing

Why Slices and Range Exclude the Last Item


In [6]:
l = [10, 20, 30, 40, 50, 60]

In [7]:
l[:2]


Out[7]:
[10, 20]

In [8]:
l[2:]


Out[8]:
[30, 40, 50, 60]

In [9]:
l[:3]


Out[9]:
[10, 20, 30]

In [10]:
l[3:]


Out[10]:
[40, 50, 60]

Slice Objects


In [11]:
s = 'bicycle'

In [12]:
s[::3]


Out[12]:
'bye'

In [13]:
s[::1]


Out[13]:
'bicycle'

In [15]:
s[::-2]


Out[15]:
'eccb'

In [14]:
invoice = """
0.....6.................................40...........52..55..........
1909  Pimoroni PiBrella                      $17.50      3      $52.50
1489  6mm Tactile Switch x20                  $4.95      2       $9.90
1510  Panavise Jr. - PV-201                  $28.00      1      $28.00
1601  PiTFT Mini Kit 320x240                 $34.95      1      $34.95
"""

In [2]:
invoice


Out[2]:
'\n0.....6.................................40...........52..55..........\n1909 Pimoroni PiBrella                      $17.50      3      $52.50\n1489 6mm Tactile Switch x20                  $4.95      2       $9.90\n1510 Panavise Jr. - PV-201                  $28.00      1      $28.00\n1601 PiTFT Mini Kit 320x240                 $34.95      1      $34.95\n'

In [3]:
SKU = slice(0,6)

In [4]:
SKU


Out[4]:
slice(0, 6, None)

In [5]:
DESCRIPTION = slice(6,40)

In [6]:
UNIT_PRICE = slice(52,55)

In [7]:
QUANTITY = slice(55, 55)

In [8]:
ITEM_TOTAL = slice(55, None)

In [15]:
line_items = invoice.split('\n')[2:]

In [16]:
line_items


Out[16]:
['1909  Pimoroni PiBrella                      $17.50      3      $52.50',
 '1489  6mm Tactile Switch x20                  $4.95      2       $9.90',
 '1510  Panavise Jr. - PV-201                  $28.00      1      $28.00',
 '1601  PiTFT Mini Kit 320x240                 $34.95      1      $34.95',
 '']

In [17]:
for item in line_items:
    print(item[UNIT_PRICE], item[DESCRIPTION])


    Pimoroni PiBrella                 
    6mm Tactile Switch x20            
    Panavise Jr. - PV-201             
    PiTFT Mini Kit 320x240            
 

Assigning to Slices


In [18]:
l = list(range(10))

In [19]:
l


Out[19]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

In [22]:
l[2:5] = [20,30]

In [23]:
l


Out[23]:
[0, 1, 20, 30, 5, 6, 7, 8, 9]

In [25]:
del l[5:7]

In [26]:
l


Out[26]:
[0, 1, 20, 30, 5, 8, 9]

In [28]:
l[3::2] = [11,22]

In [29]:
l


Out[29]:
[0, 1, 20, 11, 5, 22, 9]

In [30]:
l[2:5] = 100


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-30-e500d102d83b> in <module>()
----> 1 l[2:5] = 100

TypeError: can only assign an iterable

In [31]:
l[2:5] = [100]

In [32]:
l


Out[32]:
[0, 1, 100, 22, 9]

Using + and * with Sequences


In [33]:
l = [1,2,3]

In [34]:
l * 5


Out[34]:
[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

In [35]:
5 * 'abcd'


Out[35]:
'abcdabcdabcdabcdabcd'

Building Lists of Lists


In [36]:
board = [['_'] * 3 for i in range(3)]

In [37]:
board


Out[37]:
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]

In [38]:
board[1][2] = 'X'

In [39]:
board


Out[39]:
[['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]

In [40]:
weird_board = [['_'] * 3] * 3

In [41]:
weird_board


Out[41]:
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]

In [42]:
weird_board[1][2] = '0'

In [43]:
weird_board


Out[43]:
[['_', '_', '0'], ['_', '_', '0'], ['_', '_', '0']]

In [57]:
row = ['_'] * 3
board = []
for i in range(3):
    board.append(row)

In [58]:
board


Out[58]:
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]

In [59]:
board[2][0] = 'X'

In [60]:
board


Out[60]:
[['X', '_', '_'], ['X', '_', '_'], ['X', '_', '_']]

In [53]:
board = []
for i in range(3):
    row = ['_'] * 3
    board.append(row)

In [54]:
board


Out[54]:
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]

In [55]:
board[2][0] = 'X'

In [56]:
board


Out[56]:
[['_', '_', '_'], ['_', '_', '_'], ['X', '_', '_']]

Augmented Assignment with Sequences


In [1]:
l = [1, 2, 3]

In [2]:
id(l)


Out[2]:
744635471112

In [3]:
l *=2

In [4]:
l


Out[4]:
[1, 2, 3, 1, 2, 3]

In [5]:
id(l)


Out[5]:
744635471112

In [6]:
t = (1, 2, 3)

In [7]:
id(t)


Out[7]:
744650701704

In [8]:
t *= 2

In [9]:
t


Out[9]:
(1, 2, 3, 1, 2, 3)

In [10]:
id(t)


Out[10]:
744648070824

A += Assignment Puzzler


In [11]:
t = (1, 2, [30, 40])

In [15]:
t[2] += [50, 60]


---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-9f190ddb433c> in <module>()
----> 1 t[2] += [50, 60]

TypeError: 'tuple' object does not support item assignment

In [16]:
t


Out[16]:
(1, 2, [30, 40, 50, 60])

In [22]:
import dis
dis.dis('s[a] += b')


  1           0 LOAD_NAME                0 (s)
              3 LOAD_NAME                1 (a)
              6 DUP_TOP_TWO
              7 BINARY_SUBSCR
              8 LOAD_NAME                2 (b)
             11 INPLACE_ADD
             12 ROT_THREE
             13 STORE_SUBSCR
             14 LOAD_CONST               0 (None)
             17 RETURN_VALUE

list.sort and the sorted Built-In Function


In [23]:
fruits = ['grape', 'raspberry', 'apple', 'banana']

In [24]:
sorted(fruits)


Out[24]:
['apple', 'banana', 'grape', 'raspberry']

In [25]:
fruits


Out[25]:
['grape', 'raspberry', 'apple', 'banana']

In [26]:
sorted(fruits, reverse = True)


Out[26]:
['raspberry', 'grape', 'banana', 'apple']

In [27]:
sorted(fruits, key=len)


Out[27]:
['grape', 'apple', 'banana', 'raspberry']

In [28]:
sorted(fruits, key=len, reverse=True)


Out[28]:
['raspberry', 'banana', 'grape', 'apple']

In [29]:
fruits


Out[29]:
['grape', 'raspberry', 'apple', 'banana']

In [30]:
fruits.sort()

In [31]:
fruits


Out[31]:
['apple', 'banana', 'grape', 'raspberry']

Managing Ordered Sequences with bisect

Searching with bisect


In [32]:
import bisect
import sys

In [33]:
HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 26, 29, 30]
NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]

In [60]:
ROW_FMT = '{0:2d} @ {1:2d}   {2}{0:<2d}'

In [53]:
def demo(bisect_fn):
    for needle in reversed(NEEDLES):
        position = bisect_fn(HAYSTACK, needle)
        offset = position * '  |'
        print(ROW_FMT.format(needle, position, offset))

In [66]:
bisect_fn = bisect.bisect
print('DEMO:', bisect_fn.__name__)
print('haystack->', ' '.join('%2d' % n for n in HAYSTACK))
demo(bisect_fn)


DEMO: bisect
haystack->  1  4  5  6  8 12 15 20 21 23 26 29 30
31 @ 13     |  |  |  |  |  |  |  |  |  |  |  |  |31
30 @ 13     |  |  |  |  |  |  |  |  |  |  |  |  |30
29 @ 12     |  |  |  |  |  |  |  |  |  |  |  |29
23 @ 10     |  |  |  |  |  |  |  |  |  |23
22 @  9     |  |  |  |  |  |  |  |  |22
10 @  5     |  |  |  |  |10
 8 @  5     |  |  |  |  |8 
 5 @  3     |  |  |5 
 2 @  1     |2 
 1 @  1     |1 
 0 @  0   0 

In [65]:
bisect_fn = bisect.bisect_left
print('DEMO:', bisect_fn.__name__)
print('haystack->', ' '.join('%2d' % n for n in HAYSTACK))
demo(bisect_fn)


DEMO: bisect_left
haystack->  1  4  5  6  8 12 15 20 21 23 26 29 30
31 @ 13     |  |  |  |  |  |  |  |  |  |  |  |  |31
30 @ 12     |  |  |  |  |  |  |  |  |  |  |  |30
29 @ 11     |  |  |  |  |  |  |  |  |  |  |29
23 @  9     |  |  |  |  |  |  |  |  |23
22 @  9     |  |  |  |  |  |  |  |  |22
10 @  5     |  |  |  |  |10
 8 @  4     |  |  |  |8 
 5 @  2     |  |5 
 2 @  1     |2 
 1 @  0   1 
 0 @  0   0 

In [67]:
def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    i = bisect.bisect(breakpoints, score)
    return grades[i]

In [68]:
[grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]


Out[68]:
['F', 'A', 'C', 'C', 'B', 'A', 'A']

Inserting with bisect.insort


In [86]:
import bisect
import random

In [87]:
SIZE = 7

In [88]:
random.seed(1729)

In [89]:
my_list =[]
for i in range(SIZE):
    new_item = random.randrange(SIZE*2)
    bisect.insort(my_list, new_item)
    print('%2d ->' % new_item, my_list )


10 -> [10]
 0 -> [0, 10]
 6 -> [0, 6, 10]
 8 -> [0, 6, 8, 10]
 7 -> [0, 6, 7, 8, 10]
 2 -> [0, 2, 6, 7, 8, 10]
10 -> [0, 2, 6, 7, 8, 10, 10]

When a List is Not the Answer

Arrays


In [33]:
from array import array
from random import random

In [34]:
floats = array('d', (random() for i in range(10**7)))

In [35]:
floats[-1]


Out[35]:
0.5451360798336022

In [36]:
fp = open('floats.bin', 'wb')

In [37]:
floats.tofile(fp)

In [38]:
fp.close()

In [7]:
floats2 = array('d')

In [8]:
fp = open('floats.bin','rb')

In [9]:
floats2.fromfile(fp,10**7)

In [10]:
fp.close()

In [11]:
floats2[-1]


Out[11]:
0.2495075550059842

In [12]:
floats2 == floats


Out[12]:
True

Memory Views


In [2]:
import array
numbers = array.array('h', [-2, -1, 0, 1, 2])

In [3]:
memv = memoryview(numbers)

In [4]:
len(memv)


Out[4]:
5

In [5]:
memv[0]


Out[5]:
-2

In [7]:
memv_oct = memv.cast('B')

In [8]:
memv_oct.tolist()


Out[8]:
[254, 255, 255, 255, 0, 0, 1, 0, 2, 0]

In [10]:
memv_oct[5] = 4

In [11]:
numbers


Out[11]:
array('h', [-2, -1, 1024, 1, 2])

NumPy and SciPy


In [15]:
import numpy

In [16]:
a = numpy.arange(12)

In [17]:
a


Out[17]:
array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])

In [18]:
type(a)


Out[18]:
numpy.ndarray

In [19]:
a.shape


Out[19]:
(12,)

In [20]:
a.shape = 3, 4

In [21]:
a


Out[21]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [22]:
a[2]


Out[22]:
array([ 8,  9, 10, 11])

In [23]:
a[2,1]


Out[23]:
9

In [24]:
a[:,1]


Out[24]:
array([1, 5, 9])

In [25]:
a.transpose()


Out[25]:
array([[ 0,  4,  8],
       [ 1,  5,  9],
       [ 2,  6, 10],
       [ 3,  7, 11]])

In [59]:
floats = array('d')
fp = open('floats.bin','rb')
floats.fromfile(fp,10**6)
fp.close()

In [60]:
floats = numpy.asarray(floats)

In [61]:
floats


Out[61]:
array([ 0.63800449,  0.43174904,  0.72977767, ...,  0.78848915,
        0.62317728,  0.05762697])

In [62]:
floats[-3:]


Out[62]:
array([ 0.78848915,  0.62317728,  0.05762697])

In [63]:
floats *= .5

In [64]:
floats[-3:]


Out[64]:
array([ 0.39424457,  0.31158864,  0.02881349])

In [65]:
from time import perf_counter as pc

In [66]:
t0 = pc(); floats /=3; pc() - t0


Out[66]:
0.0030416026502280147

In [67]:
numpy.save('floats-10M', floats)


---------------------------------------------------------------------------
OSError                                   Traceback (most recent call last)
<ipython-input-67-d5a03c5ab968> in <module>()
----> 1 numpy.save('floats-10M', floats)

c:\users\langestrst01\appdata\local\continuum\anaconda3\envs\fluentpy\lib\site-packages\numpy\lib\npyio.py in save(file, arr, allow_pickle, fix_imports)
    475         if not file.endswith('.npy'):
    476             file = file + '.npy'
--> 477         fid = open(file, "wb")
    478         own_fid = True
    479     else:

OSError: [Errno 22] Invalid argument: 'floats-10M.npy'

In [52]:
floats2 = numpy.load('floats-10M.npy', 'r+')

In [53]:
floats2 *= 6

In [54]:
floats2[-3:]


Out[54]:
memmap([ 1.5769783 ,  1.24635457,  0.11525394])

Deques and Other Queues


In [70]:
from collections import deque

In [71]:
dq = deque(range(10), maxlen=10)

In [72]:
dq


Out[72]:
deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [73]:
dq.rotate(3)

In [74]:
dq


Out[74]:
deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])

In [75]:
dq.rotate(-4)

In [76]:
dq


Out[76]:
deque([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])

In [77]:
dq.appendleft(-1)

In [78]:
dq


Out[78]:
deque([-1, 1, 2, 3, 4, 5, 6, 7, 8, 9])

In [79]:
dq.extend([11, 22, 33])

In [80]:
dq


Out[80]:
deque([3, 4, 5, 6, 7, 8, 9, 11, 22, 33])

In [81]:
dq.extendleft([10, 20, 30, 40])

In [82]:
dq


Out[82]:
deque([40, 30, 20, 10, 3, 4, 5, 6, 7, 8])

In [ ]: